﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using WAV.Utility;

namespace USART
{
    public partial class theMain : Form
    {
     //   const int DataRequir = 0x58;
        delegate void Rec(string data);
        // delegate int TransmitFileInvoke(byte[] data, int transmitNum, bool TransmitAllow, string CMD);
        Thread threadTransmitProcedure;
        public theMain()
        {
            InitializeComponent();

        }
        public static SerialPort Comm;

        private void Form1_Load(object sender, EventArgs e)
        {

            GroupBox1_Init();
            serialPort1_Init();
        }
        //0 不启动  
        //1 发送握手 收到  11握手成功 0失败
        //2 握手成功发送文件信息  21接受成功 20失败
        //3 发送文件数据
        private void processTransmit(object obj)
        {
            while (transmitStep != 0)
            {
                switch (transmitStep)
                {
                    case 11:
                        {
                            transmitStep = 10;
                            byte[] msg1 = {  0xAE,0xAF, // Header  Master-->Slave
                                             0,0x06,     //Length   uint16_t
                                             0x01,        //CMD  
                                             0xF5        //CRC8
                                        };
                            Comm.Write(msg1, 0, msg1.Length);
                        }
                        break;
                    case 21:
                        {
                            try
                            {
                                transmitStep = 20;
                                byte[] msg2 = { 0xAE,0xAF, // Header  Master-->Slave
                                                0,0x0A,     //Length   uint16_t
                                                0x02,        //CMD  Send File info
                                                0,0,0,0,  //data size
                                                0          //CRC8
                                            };
                                msg2[5] = (byte)(fileSize >> 24); msg2[6] = (byte)(fileSize >> 16);
                                msg2[7] = (byte)(fileSize >> 8); msg2[8] = (byte)(fileSize);
                                msg2[9] = CRC.CRC8Calculate(msg2, 9);
                                Comm.Write(msg2, 0, msg2.Length);
                            }
                            catch (Exception e)
                            {
                                MessageBox.Show(e.Message);
                            }

                        }
                        break;
                    case 3:
                        {
                            byte[] msg3_template = {  0xAE,0xAF, // Header  Master-->Slave
                                         //   0,0,     //Length   uint16_t
                                            0x03,        //CMD  Send File info
                                            0,0,0,0     //page
                                        };
                            byte msg3_CRC = 0; 
                            msg3_CRC = 0;
                            List<byte> lb = new List<byte>();
                            uint length = 0;
                            uint offsetPage = 0;
                            if (GetApplyPage(Comm, (uint)data.Length, ref offsetPage, ref length))
                            {
                                msg3_template[3] = (byte)(offsetPage >> 24);
                                msg3_template[4] = (byte)(offsetPage >> 16);
                                msg3_template[5] = (byte)(offsetPage >> 8);
                                msg3_template[6] = (byte)(offsetPage);
                                lb.Clear();
                                lb.AddRange(msg3_template);

                                lb.Insert(2, (byte)((lb.Count + length + 3) >> 8));
                                lb.Insert(3, (byte)(lb.Count + length + 2));

                                for (int i = 0; i < lb.Count; i++)
                                {
                                    msg3_CRC = CRC.CRC8CalculateByte(msg3_CRC, lb[i]);
                                }

                                for (uint i = offsetPage; i < offsetPage + length; i++) //4KB--16 page
                                {
                                    lb.Add(data[i]);
                                    msg3_CRC = CRC.CRC8CalculateByte(msg3_CRC, data[i]);
                                }
                                lb.Add(msg3_CRC);
                                TransmitMessage(Comm, lb.ToArray());
                            }
                        }
                        break;

                    default:
                        break;
                }
            }
        }
        bool waitOffsetApply = true;
        private bool GetApplyPage(SerialPort _ob, uint totalSize, ref uint offsetPage, ref uint length)
        {
            if (!waitOffsetApply)
            {
                waitOffsetApply = true;
                offsetPage = (uint)(messageVaild[5] << 24 | messageVaild[6] << 16 | messageVaild[7] << 8 | messageVaild[8]);
                length = (uint)(messageVaild[9] << 24 | messageVaild[10] << 16 | messageVaild[11] << 8 | messageVaild[12]);
                if (offsetPage > totalSize)
                {
                    byte[] msg3_template = {  0xAE,0xAF, // Header  Master-->Slave
                                            0,6,     //Length   uint16_t
                                            0x44,        //CMD   
                                            0x01,
                                            0xb3
                                        };
                    _ob.Write(msg3_template, 0, msg3_template.Length);
                    return false;
                }
                if (length > totalSize - offsetPage)
                {
                    byte[] msg3_template = {  0xAE,0xAF, // Header  Master-->Slave
                                          0,7,     //Length   uint16_t
                                          0x44,        //CMD     
                                          0x02,0xba
                                        };
                    _ob.Write(msg3_template, 0, msg3_template.Length);
                    return false;
                }
                return true;
            }
            else
                return false;
            
        }


        /// <summary>
        /// Split into a 2k size block & forward
        /// </summary>
        /// <param name="_ob"></param>
        /// <param name="bb"></param>
        const int PACK_SIZE = 2048;
        private void TransmitMessage(SerialPort _ob, byte[] bb)
        {
            if (_ob.IsOpen)
            {
                int offset = 0;
                while (offset < bb.Length)
                {
                    Thread.Sleep(15);
                    // while (!_ob.BaseStream.CanWrite) ;
                    if (bb.Length - offset > PACK_SIZE)
                    {
                        try
                        {
                            //_ob.Write(bb, offset, 2048);
                            _ob.BaseStream.Write(bb, offset, PACK_SIZE);
                        }
                        catch (Exception e)
                        {
                            MessageBox.Show(e.ToString());
                        }
                        offset += PACK_SIZE;
                    }
                    else
                    {
                        try
                        {
                            // _ob.Write(bb, offset, bb.Length - offset);
                            _ob.BaseStream.Write(bb, offset, bb.Length - offset);
                        }
                        catch (Exception e)
                        {
                            MessageBox.Show(e.ToString());
                        }
                        offset = bb.Length;
                    }

                }
            }
        }

        private void serialPort1_Init()
        {
            Comm = new SerialPort();
            Comm.NewLine = "/r/n";
            Comm.DataReceived += new SerialDataReceivedEventHandler(SerialRec);
            Comm.ReadTimeout = 200;
        }

        /// <summary>
        /// 接收线程
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        #region

        //       byte[] msgRec=new byte[111];
        bool allowProcess = false;
        //      bool header1Detected = false, header2Detected = false, allowProcess=false;
        //     static int recCount = 0, recLength=0; 
        List<byte> messageBuffer = new List<byte>();
        List<byte> messageVaild = new List<byte>();
        private void SerialRec(object sender, SerialDataReceivedEventArgs e)
        {
            if (Comm.IsOpen)
            {

                byte[] by1 = new byte[Comm.BytesToRead];
                Comm.Read(by1, 0, by1.Length);
                messageBuffer.AddRange(by1);
                while (messageBuffer.Count >= 4) //至少包含帧头（2字节）、长度（1字节）、校验位（1字节）；根据设计不同而不同
                {
                    //2.1 查找数据头
                    if (messageBuffer[0] == 0xEA && messageBuffer[1] == 0xFA) //传输数据有帧头，用于判断
                    {
                        int len = messageBuffer[2] << 8 | messageBuffer[3];
                        if (messageBuffer.Count < len) //数据区尚未接收完整
                        {
                            break;
                        }
                        byte crc = 0;
                        foreach (byte item in messageBuffer)
                        {
                            crc = CRC.CRC8CalculateByte(crc, item);
                        }
                      /*  if (messageBuffer[4]==3)
                        {
                            MessageBox.Show(messageBuffer[4].ToString());
                        } */
                        if (crc==0)
                        {
                            //得到完整的数据，复制到ReceiveBytes中进行校验
                            // messageBuffer.CopyTo(0, msgRec, 0, len);
                            messageVaild.Clear();
                            for (int i = 0; i < len; i++)
                            {
                                messageVaild.Add(messageBuffer[i]);
                            }
                            allowProcess = true;
                        }
                    
                        /*     byte jiaoyan; //开始校验
                             jiaoyan = this.JY(ReceiveBytes);
                             if (jiaoyan != ReceiveBytes[len + 3]) //校验失败，最后一个字节是校验位
                             {
                                 buffer.RemoveRange(0, len + 4);
                                 MessageBox.Show("数据包不正确！");
                                 continue;
                             }*/
                        messageBuffer.RemoveRange(0, len);
                        /////执行其他代码，对数据进行处理。
                    }
                    else //帧头不正确时，记得清除
                    {
                        messageBuffer.RemoveAt(0);
                    }
                }

                if (allowProcess)
                {
                    allowProcess = false;

                    if (transmitStep != 0) // Allowed to analyse
                    {
                        if (messageVaild[0] == 0xEA && messageVaild[1] == 0xFA)
                        {
                            switch (messageVaild[4])
                            {
                                case 0x01:
                                    //   if (transmitStep != 10)
                                    {
                                        if (messageVaild[5] == 0x01)
                                            transmitStep = 21;
                                        else
                                            transmitStep = 0;
                                    }
                                    break;
                                case 0x02:
                                    if (transmitStep != 10)
                                    {
                                        if (messageVaild[5] == 0x01)
                                            transmitStep = 3;
                                        else
                                            transmitStep = 0;
                                    }
                                    break;
                                case 0x03:
                                    if (transmitStep == 3 && messageVaild.Count > 8) //EA FA 00 00 03 00 00 00 00 
                                    {
                                        waitOffsetApply = false;
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                }

                string rr = "a4";
                if (messageVaild.Count > 0)
                {
                    BeginInvoke(new Rec(UI_Update), new object[] { rr /*messageVaild.ToArray()*/ });
                }
            }
        }



        #endregion
        #region UI显示更新

        public void UI_Update(object recdata)
        {
            // 设置焦点到richTextBoxReceive
            //richTextBoxReceive.Focus();
            // 需要手动设置richTextBox的滚动条，否则不会自动更新,只显示最开始的内容
            // 设置光标的位置到文本尾   
            /*         richTextBoxRec.Select(richTextBoxRec.TextLength, 0);
                     // 滚动到控件光标处   
                     richTextBoxRec.ScrollToCaret();
                     if (checkBoxHexRec.Checked)
                     {
                         StringBuilder sb = new StringBuilder();
                         sb.Append(BitConverter.ToString((byte[])recdata,0).Replace("-"," "));
                     /*    foreach (short item in recdata.ToString().ToCharArray())
                         {
                             sb.Append(item.ToString("x2"));//string.Format("X", item));
                             sb.Append(" ");
                         }*/
            // 在末尾添加新内容
            /*       richTextBoxRec.AppendText(sb.ToString());
               }
               else
                   richTextBoxRec.AppendText(recdata.ToString());
           */

        }
        #endregion
        #region 设置面板初始化

        private void GroupBox1_Init()
        {

            cBoxCom.DataSource = SerialPort.GetPortNames();
            List<string> list = new List<string>();
            list.Insert(0, "4800");
            list.Insert(1, "9600");
            list.Insert(2, "19200");
            list.Insert(3, "115200");
            list.Insert(4, "125000"); 
            list.Insert(5, "128000");                              
            list.Insert(6, "230400");          
            list.Insert(7, "250000");
            list.Insert(8, "256000");  
            list.Insert(9, "312500");
            list.Insert(10, "384000"); 
            list.Insert(11,  "500000");
            cBoxBaudRate.DataSource = list.ToArray();
            cBoxBaudRate.SelectedIndex = 11;
            cBoxCom.DropDownStyle = ComboBoxStyle.DropDownList;
            list.Clear();
            list.Insert(0, "4");
            list.Insert(1, "5");
            list.Insert(2, "6");
            list.Insert(3, "7");
            list.Insert(4, "8");
            cBoxDataBit.DataSource = list.ToArray();
            cBoxDataBit.SelectedIndex = 4;
            cBoxDataBit.DropDownStyle = ComboBoxStyle.DropDownList;
            list.Clear();
            list.Insert(0, "偶");
            list.Insert(1, "奇");
            list.Insert(2, "无");
            cBoxCompart.DataSource = list.ToArray();
            cBoxCompart.SelectedIndex = 2;
            cBoxCompart.DropDownStyle = ComboBoxStyle.DropDownList;
            list.Clear();
            list.Insert(0, "0");
            list.Insert(1, "1");
            list.Insert(2, "1.5");
            list.Insert(3, "2");
            cBoxStopBit.DataSource = list.ToArray();
            cBoxStopBit.SelectedIndex = 1;
            cBoxStopBit.DropDownStyle = ComboBoxStyle.DropDownList;
            btnOption.Text = "打开";
            btnStatus.Text = "状态：关闭";
            btnStatus.BackColor = Color.Red;


        }

        private void btnOption_Click(object sender, EventArgs e)
        {
            if (btnOption.Text != "关闭")
            {
                if (string.IsNullOrEmpty(cBoxCom.Text)) { MessageBox.Show("没有可打开的串口"); return; }
                #region
                if (Comm.IsOpen)
                {
                    Comm.Close();
                }
                else
                {
                    //  Comm.WriteBufferSize = 10 * 1024;
                    //  Comm.ReadBufferSize = 10 * 1024;
                    Comm.RtsEnable = false;

                    Comm.PortName = cBoxCom.SelectedValue.ToString();
                    Comm.BaudRate = Convert.ToInt32(cBoxBaudRate.Text);
                    Comm.DataBits = Convert.ToInt32(cBoxDataBit.Text);
                    switch (cBoxCompart.Text)
                    {
                        case "偶": Comm.Parity = Parity.Even;
                            break;
                        case "奇": Comm.Parity = Parity.Mark;
                            break;
                        case "无": Comm.Parity = Parity.None;
                            break;
                    }
                    switch (cBoxStopBit.Text)
                    {
                        case "0": Comm.StopBits = StopBits.None;
                            break;
                        case "1": Comm.StopBits = StopBits.One;
                            break;
                        case "1.5": Comm.StopBits = StopBits.OnePointFive;
                            break;
                        case "2": Comm.StopBits = StopBits.Two;
                            break;
                    }

                    try
                    {
                        Comm.Open();

                        btnStatus.Text = "状态：打开";
                        btnStatus.BackColor = Color.Green;
                        btnOption.Text = "关闭";
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("系统异常，打开失败！");
                        btnStatus.Text = "状态：关闭";
                        btnStatus.BackColor = Color.Red;
                        Comm = new SerialPort();
                    }
                }
                #endregion
            }
            else
            {
                if (Comm.IsOpen)
                {
                    Comm.Close();
                    btnOption.Text = "打开";
                    btnStatus.Text = "状态：关闭";
                    btnStatus.BackColor = Color.Red;
                }
                if (threadTransmitProcedure != null)
                {
                    threadTransmitProcedure.Abort();
                    transmitStep = 0;
                }
            }
        }
        #endregion
        #region 发送按钮

        private void btnTransmit_Click(object sender, EventArgs e)
        {
            if (!Comm.IsOpen) { MessageBox.Show("串口未打开"); return; }
            if (checkBoxHexTransmit.Checked)
            {
                byte[] by = StringToByte(richTextBoxTransmit.Text.Replace(" ", ""));
                Comm.Write(by, 0, by.Length);
            }
            else
            {
                Comm.Write(richTextBoxTransmit.Text.Replace(" ", ""));
            }
        }
        #endregion


        /// <summary>
        /// 将输入为十六进制的字符串转换为数字
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        #region

        public byte[] StringToByte(string data)
        {
            List<byte> bdata = new List<byte>();
            char[] bufdata = data.ToCharArray();
            if (data.Length % 2 == 0)
            {
                byte buf1 = 0;

                for (int i = 0; i < data.Length - 1; )
                {
                    buf1 = OneStringtoByte(bufdata[i]);
                    buf1 <<= 4;
                    buf1 |= OneStringtoByte(bufdata[i + 1]);
                    bdata.Add(buf1);
                    i += 2;
                }
            }
            else
            {
                byte buf1 = 0;
                for (int i = 0; i < data.Length - 2; )
                {
                    buf1 = OneStringtoByte(bufdata[i]);
                    buf1 <<= 4;
                    buf1 |= OneStringtoByte(bufdata[i + 1]);
                    bdata.Add(buf1);
                    i += 2;
                }
                bdata.Add(OneStringtoByte(bufdata[bufdata.Length - 1]));
            }
            return bdata.ToArray();
        }
        public byte OneStringtoByte(char i)
        {
            switch (i)
            {
                case '0': return 0;
                case '1': return 1;
                case '2': return 2;
                case '3': return 3;
                case '4': return 4;
                case '5': return 5;
                case '6': return 6;
                case '7': return 7;
                case '8': return 8;
                case '9': return 9;
                case 'a': return 0x0a;
                case 'b': return 0x0b;
                case 'c': return 0x0c;
                case 'd': return 0x0d;
                case 'e': return 0x0e;
                case 'f': return 0x0f;
                case 'A': return 0x0a;
                case 'B': return 0x0b;
                case 'C': return 0x0c;
                case 'D': return 0x0d;
                case 'E': return 0x0e;
                case 'F': return 0x0f;
                default: return 0;
            }
        }
        public float[] TwoByteToFloat(byte[] by)
        {

            List<float> bdata = new List<float>();

            if (by.Length % 2 == 0)
            {
                ushort buf1 = 0;

                for (int i = 0; i < by.Length - 1; )
                {
                    buf1 = by[i];
                    buf1 <<= 8;
                    buf1 |= by[i + 1];
                    bdata.Add(buf1);
                    i += 2;
                }
            }
            else
            {
                ushort buf1 = 0;
                for (int i = 0; i < by.Length - 2; )
                {
                    buf1 = by[i];
                    buf1 <<= 4;
                    buf1 |= by[i + 1];
                    bdata.Add(buf1);
                    i += 2;
                }
                bdata.Add(by[by.Length - 1]);
            }
            return bdata.ToArray();
        }
        public string ByteToString(byte[] data)
        {
            string hexOutput = "";
            for (int i = 0; i < data.Length; i++)
            {
                hexOutput += string.Format("{0:X2}", data[i]) + " ";
            }
            return hexOutput;
        }
        #endregion

        #region 清除按钮

        private void btnClearTransmit_Click(object sender, EventArgs e)
        {
            richTextBoxTransmit.Clear();


        }

        private void btnClearRec_Click(object sender, EventArgs e)
        {
            richTextBoxRec.Clear();
        }
        #endregion

        #region 数据发送


        static string filePath = "";
        static byte[] data;
        static uint pageNum = 0;
        static uint fileSize = 0;
        DataTable dt = new DataTable("Binary");
        private void btnOpenFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Multiselect = false;//允许打开多个文件
            if (dialog.ShowDialog() == DialogResult.OK)
                filePath = textFilePath.Text = dialog.FileName;
            data = GetFileBinary(filePath);
            // richBoxData.Text = ByteToString(data);
            //richBoxData.Text = BitConverter.ToString(data).Replace("-"," ");
            fileSize = (uint)data.Length;

            pageNum = (uint)data.Length / 256;
            if (0 != data.Length % 256)
            {
                pageNum += 1;
            }
            UpdateDataViewerGrid();
            labelFileSizeShow.Text = data.Length.ToString();
          //  string crc = String.Format("{0:X}", CRC.CRC8Calculate(data, 100));
        }
        void UpdateDataViewerGrid()
        {
            dt.Dispose();
            dt = new DataTable();
            dt.Columns.Add(new DataColumn("Offset", typeof(string)));

            //Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
            for (int i = 0; i < 16; i++)
            {
                // string ii = String.Format("{0:X2}", i);
                dt.Columns.Add(new DataColumn(String.Format("{0:X2}", i), typeof(byte)));
                //  dataGridView1.Columns[i + 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
            }
            int index = 0;

            while (index < data.Length)
            {
                DataRow dr = dt.NewRow();
                if (data.Length - index > 16)
                {
                    dr[0] = String.Format("{0:X6}", index);// dr[0] = index / 16;
                    for (int j = 0; j < 16; j++)
                    {
                        dr[j + 1] = data[j + index];
                    }
                    dt.Rows.Add(dr);

                    index += 16;
                }
                else
                {
                    dr[0] = String.Format("{0:X6}", index + 1);
                    for (int j = 0; j < data.Length - index; j++)
                    {
                        dr[j + 1] = data[j + index];
                    }
                    dt.Rows.Add(dr);
                    index = data.Length;
                }
            }
            dataGridView1.DataSource = dt;
            for (int i = 0; i < 17; i++)
            {
                dataGridView1.Columns[i].DefaultCellStyle.BackColor = System.Drawing.SystemColors.ActiveCaption;
                dataGridView1.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                dataGridView1.Columns[i].DefaultCellStyle.Format = "X";
            }
            // dataGridView1.Columns[0].DefaultCellStyle.Format = "{0:X6}";
            dataGridView1.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
        }
        private byte[] GetFileBinary(string filePath)
        {
            FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            try
            {
                byte[] buffur = new byte[fs.Length];
                fs.Read(buffur, 0, (int)fs.Length);

                return buffur;
            }
            catch (Exception ex)
            {
                //MessageBoxHelper.ShowPrompt(ex.Message);
                return null;
            }
            finally
            {
                if (fs != null)
                {

                    //关闭资源
                    fs.Close();
                }
            }

        }

        int transmitStep = 0;
        List<byte> msg = new List<byte>();
        //byte[] msg = {  0xAE,0xAF, // Header  Master-->Slave
        //                0,0,     //Length   uint16_t
        //                0        //CMD
        //            //0,  data
        //            };
        private void btnTransmitData_Click(object sender, EventArgs e)
        {
            if (Comm.IsOpen)
            {
                if (data.Length != 0)
                {
                    if (transmitStep == 0)
                    {
                        if (threadTransmitProcedure != null) threadTransmitProcedure.Abort();
                        transmitStep = 11;
                        threadTransmitProcedure = new Thread(processTransmit);
                        threadTransmitProcedure.Start();

                    }


                }
            }
        }
        #endregion

        private void theMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (threadTransmitProcedure != null)
            {
                threadTransmitProcedure.Abort();
            }
        }


    }
}
